package com.xuecheng.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.xuecheng.api.order.model.dto.OrdersDTO;
import com.xuecheng.api.order.model.dto.PayResultModel;
import com.xuecheng.api.search.dto.CoursePubIndexDTO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseChargeEnum;
import com.xuecheng.common.enums.order.OrderDealStatusEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.PaymentUtil;
import com.xuecheng.order.agent.CoursePubSearchApiAgent;
import com.xuecheng.order.common.constant.OrderErrorCode;
import com.xuecheng.order.convert.OrdersConvert;
import com.xuecheng.order.entity.Orders;
import com.xuecheng.order.entity.Pay;
import com.xuecheng.order.mapper.OrdersMapper;
import com.xuecheng.order.service.OrdersService;
import com.xuecheng.order.service.PayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements OrdersService {

    @Autowired
    private CoursePubSearchApiAgent agent;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private PayService payService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Value("${rocketmq.massage.topic}")
    private String topic;

    @Override
    @Transactional
    public OrdersDTO createOrder(Long coursePubId, String username) {

        //校验课程发布信息是否存在
        RestResponse<CoursePubIndexDTO> restResponse = agent.getCoursePubById(coursePubId);
        if (!restResponse.isSuccessful()) {
            ExceptionCast.cast(OrderErrorCode.E_160002);
        }
        CoursePubIndexDTO result = restResponse.getResult();
        if (ObjectUtils.isEmpty(result)) {
            ExceptionCast.cast(OrderErrorCode.E_160007);
        }
        //判断是否收费，如果不收费不能创建订单
        String charge = result.getCharge();
        if (!ObjectUtils.nullSafeEquals(charge, CourseChargeEnum.CHARGE_YES.getCode())) {
            ExceptionCast.cast(OrderErrorCode.E_160009);
        }
        //查询订单，不存在则创建，存在则判断状态，初始态则直接返回
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.lambdaQuery(Orders.class)
                .eq(Orders::getUserName, username)
                .eq(Orders::getCoursePubId, coursePubId);

        Orders order = this.getOne(queryWrapper);
        if (ObjectUtils.isEmpty(order)) {
            order = new Orders();
            order.setOrderNo(PaymentUtil.genUniquePayOrderNo());
            order.setCompanyId(result.getCompanyId());
            order.setCompanyName(result.getCompanyName());
            order.setCoursePubId(result.getIndexId());
            order.setCoursePubName(result.getName());
            order.setUserName(username);
            order.setPrice(result.getPrice());
            order.setInitialPrice(result.getPrice());
            String statusCode = OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode();
            order.setStatus(Integer.valueOf(statusCode));
            order.setStartTime(LocalDateTime.now());

            boolean save = this.save(order);
            if (!save) {
                ExceptionCast.cast(OrderErrorCode.E_160001);
            }
        } else {
            Integer status = order.getStatus();
            if (!OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode().equals(String.valueOf(status))) {
                ExceptionCast.cast(OrderErrorCode.E_160006);
            }
        }
        return OrdersConvert.INSTANCE.ordersToDTO(order);
    }


    /**
     * <p>
     * 创建订单支付信息
     * </p>
     *
     * @param orderNo  订单编号
     * @param username 购买用户名
     * @return Map<String, Object>
     */
    @Override
    @Transactional
    public Map<String, Object> createOrderPay(String orderNo, String username) {
        //校验订单信息
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.lambdaQuery(Orders.class)
                .eq(Orders::getOrderNo, orderNo)
                .eq(Orders::getUserName, username);
        Orders order = this.getOne(queryWrapper);
        //是否存在
        if (ObjectUtils.isEmpty(order)) {
            ExceptionCast.cast(OrderErrorCode.E_160008);
        }
        //支付状态是否为未支付
        if (!OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode().equals(String.valueOf(order.getStatus()))) {
            ExceptionCast.cast(OrderErrorCode.E_160006);
        }
        Map<String, Object> resp = new HashMap<>();
        WxPayUnifiedOrderResult orderResult;
        try {
            WxPayUnifiedOrderRequest payRequest = new WxPayUnifiedOrderRequest();
            //微信支付，要填入的参数 out_trade_no , body ,productId 商户卖出的商品id ,总金额,终端ip
            payRequest.setOutTradeNo(orderNo);
            payRequest.setBody("课程为：" + order.getCoursePubName());
            payRequest.setProductId(order.getCoursePubId().toString());

            Float price = order.getPrice();
            int totalFee = new Float(String.valueOf(price * 100)).intValue();
            payRequest.setTotalFee(totalFee);
            payRequest.setSpbillCreateIp(Inet4Address.getLocalHost().getHostAddress());

            orderResult = wxPayService.unifiedOrder(payRequest);
            //解析响应结果
            String resultCode = orderResult.getResultCode();
            String returnCode = orderResult.getReturnCode();

            //都为success才为支付成功
            if (Pay.PAY_RESPONSE_STATUS_SUCCESS.equals(resultCode) && Pay.PAY_RESPONSE_STATUS_SUCCESS.equals(returnCode)) {
                resp.put("codeURL", orderResult.getCodeURL());
                resp.put("message", orderResult.getErrCode());
                resp.put("status", Pay.PAY_STATUS_SUCCESS);
            }

        } catch (UnknownHostException | WxPayException e) {
            log.error("下单支付失败：{}", e.getMessage());
            resp.put("status", Pay.PAY_STATUS_FAIL);
            resp.put("message", "统一下单支付失败");
            return resp;
        }

        // 4.创建订单支付数据
        // 4.1 查询是否有订单支付数据
        Long orderId = order.getId();

        LambdaQueryWrapper<Pay> payQueryWrapper = Wrappers.lambdaQuery(Pay.class)
                .eq(Pay::getOrderId, orderId)
                .eq(Pay::getUserName, username);
        Pay pay = payService.getOne(payQueryWrapper);
        //没有数据才创建订单支付
        if (ObjectUtils.isEmpty(pay)) {
            boolean orderPay = this.createOrderPay(order, orderResult);
            if (!orderPay) {
                resp.put("codeURL", "");
                resp.put("message", OrderErrorCode.E_160009.getDesc());
                resp.put("status", Pay.PAY_STATUS_FAIL);
            }
        }
        return resp;
    }

    /**
     * <p>
     * 提取创建支付订单的方法
     * </p>
     *
     * @return void
     */
    private boolean createOrderPay(Orders order, WxPayUnifiedOrderResult orderResult) {
        Long orderId = order.getId();
        Pay pay = new Pay();
        pay.setOrderId(orderId);
        log.info("预支付prepay_id:{}",orderResult.getPrepayId());

        pay.setUserName(order.getUserName());
        pay.setCompanyId(order.getCompanyId());
        pay.setPayMethod(Pay.PAY_METHOD_WX);
        Float price = order.getPrice();
        pay.setTotalAmount(new BigDecimal(price));
        pay.setStatus(Pay.PAY_STATUS_UNPAID);
        return payService.save(pay);
    }

    /**
     * <p>
     * 解析通知订单 发送消息
     * </p>
     *
     * @param resultStr 支付平台返回的请求解析为字符串
     * @return void
     */
    @Override
    public String payNotify(String resultStr) {

        try {
            WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(resultStr);

            String resultCode = result.getResultCode();
            String returnCode = result.getReturnCode();

            if (!Pay.PAY_RESPONSE_STATUS_SUCCESS.equals(returnCode) ||
                    !Pay.PAY_RESPONSE_STATUS_SUCCESS.equals(resultCode)) {
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160014.getDesc());
            }

            String outTradeNo = result.getOutTradeNo();
            //查询校验订单状态，和订单支付状态

            //订单状态校验
            LambdaQueryWrapper<Orders> paidOrderQueryWrapper = Wrappers.lambdaQuery(Orders.class)
                    .eq(Orders::getOrderNo, outTradeNo)
                    .eq(Orders::getStatus, Integer.parseInt(OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode()));
            Orders order = this.getOne(paidOrderQueryWrapper);

            if (ObjectUtils.isEmpty(order)) {
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160015.getDesc());
            }
            Float price = order.getPrice(); //单位：元
            Integer totalFee = result.getTotalFee(); //单位： 分
            String toYuan = BaseWxPayResult.fenToYuan(totalFee);
            if (price != Float.parseFloat(toYuan)) {
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160016.getDesc());
            }
            Long orderId = order.getId();
            LambdaQueryWrapper<Pay> payQueryWrapper = Wrappers.lambdaQuery(Pay.class)
                    .eq(Pay::getOrderId, orderId)
                    .eq(Pay::getStatus, Pay.PAY_STATUS_UNPAID);
            Pay orderPay = payService.getOne(payQueryWrapper);
            if (ObjectUtils.isEmpty(orderPay)) {
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160015.getDesc());
            }
            //发送事务消息 返回消息发送状态
            LocalTransactionState state = sendPaymentTxMsg(result, resultCode, order, orderPay);
            if (state != LocalTransactionState.COMMIT_MESSAGE) {
                WxPayNotifyResponse.fail(OrderErrorCode.E_160011.getDesc());
            }
            return WxPayNotifyResponse.success("支付消息通知成功");
        } catch (WxPayException e) {
            log.error("异常信息：{}", e.getMessage());
            return WxPayNotifyResponse.fail("支付消息通知失败");
        }
    }

    private LocalTransactionState sendPaymentTxMsg(WxPayOrderNotifyResult result, String resultCode, Orders order, Pay orderPay) {
        //发送事务消息，封装交易结果信息
        PayResultModel model = new PayResultModel();
        OrdersDTO ordersDTO = OrdersConvert.INSTANCE.ordersToDTO(order);
        model.setOrder(ordersDTO);
        model.setPayMethod(orderPay.getPayMethod());
        String timeEnd = result.getTimeEnd();
        //设置支付时间
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        LocalDateTime payDate = LocalDateTime.parse(timeEnd, timeFormatter);
        model.setPayDate(payDate);
        model.setPayNumber(result.getTransactionId());
        model.setTradeStatus(resultCode);
        //订单应结金额
        Integer settlementTotalFee = result.getSettlementTotalFee();
        if (!ObjectUtils.isEmpty(settlementTotalFee)) {
            String yuan = BaseWxPayResult.fenToYuan(settlementTotalFee);
            model.setReceiptAmount(new BigDecimal(yuan));
        }
        //用户实付金额
        String realPayment = BaseWxPayResult.fenToYuan(result.getCashFee());
        model.setBuyerPayAmount(new BigDecimal(realPayment));
        model.setPayResponse(result.getXmlString());

        String jsonString = JSON.toJSONString(model);
        //发送事务消息
        Message<String> message = MessageBuilder.withPayload(jsonString).build();
        //判断mq消息发送结果状态
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(topic, message, null);
        LocalTransactionState state = sendResult.getLocalTransactionState();
        return state;
    }


    /**
     * <p>
     * 本地事务 更改订单状态为已支付
     * </p>
     *
     * @param payResultModel 支付信息数据模型
     * @return void
     */
    @Override
    public void paymentStatusSuccess(PayResultModel payResultModel) {

        OrdersDTO order = payResultModel.getOrder();
        Long orderId = order.getOrderId();
        int init_status = Integer.parseInt(OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode());
        LambdaQueryWrapper<Orders> ordersQueryWrapper = Wrappers.lambdaQuery(Orders.class)
                .eq(Orders::getId, orderId)
                .eq(Orders::getStatus, init_status);
        Orders orderPo = this.getOne(ordersQueryWrapper);

        if (ObjectUtils.isEmpty(orderPo)) {
            ExceptionCast.cast(OrderErrorCode.E_160017);
        }

        LambdaQueryWrapper<Pay> payQueryWrapper = Wrappers.lambdaQuery(Pay.class)
                .eq(Pay::getOrderId, orderId)
                .eq(Pay::getStatus, Pay.PAY_STATUS_UNPAID);

        Pay pay = payService.getOne(payQueryWrapper);
        if (ObjectUtils.isEmpty(pay)) {
            ExceptionCast.cast(OrderErrorCode.E_160017);
        }

        LocalDateTime payDate = payResultModel.getPayDate();

        //修改订单和订单支付的状态
        //修改订单状态
        int paid_status = Integer.parseInt(OrderDealStatusEnum.ORDER_DEAL_PAID_STATUS.getCode());
        LambdaUpdateWrapper<Orders> ordersLambdaUpdateWrapper = Wrappers.lambdaUpdate(Orders.class)
                .eq(Orders::getId, orderId)
                .set(Orders::getStatus, paid_status)
                .set(Orders::getChangeDate, LocalDateTime.now());
        boolean update = this.update(ordersLambdaUpdateWrapper);
        if (!update) {
            ExceptionCast.cast(OrderErrorCode.E_160013);
        }

        //修改订单支付状态
        LambdaUpdateWrapper<Pay> payUpdateWrapper = Wrappers.lambdaUpdate(Pay.class)
                .eq(Pay::getOrderId, orderId)
                .set(Pay::getStatus, Pay.PAY_STATUS_PAID)
                .set(Pay::getPayNumber, payResultModel.getPayNumber())
                .set(Pay::getPayDate, payDate)
                .set(Pay::getBuyerPayAmount, payResultModel.getBuyerPayAmount())
                .set(Pay::getPayResponse, payResultModel.getPayResponse())
                .set(Pay::getReceiptAmount, payResultModel.getReceiptAmount());
        update = payService.update(payUpdateWrapper);
        if (!update) {
            ExceptionCast.cast(OrderErrorCode.E_160012);
        }
    }

    /**
     * <p>
     * 根据订单编号查询订单信息
     * </p>
     *
     * @param orderNo 订单编号
     * @return OrdersDTO
     */
    @Override
    public OrdersDTO getOrderByOrderNo(String orderNo) {

        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.lambdaQuery(Orders.class)
                .eq(Orders::getOrderNo, orderNo);

        Orders order = this.getOne(queryWrapper);

        OrdersDTO ordersDTO = new OrdersDTO();

        if (!ObjectUtils.isEmpty(order)) {

            ordersDTO = OrdersConvert.INSTANCE.ordersToDTO(order);

        }
        return ordersDTO;
    }

    public static void main(String[] args) {
        String timeEnd = "20210419150026";
        //DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.valueOf("yyyyMMddHHmmss"));
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        LocalDateTime payDate = LocalDateTime.parse(timeEnd, timeFormatter);
        System.out.println(payDate);
    }
}
